package com.supply.hsa.link.task.fetch.server.impl;

import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.fangcang.common.util.DateUtil;
import com.fangcang.common.util.PropertyCopyUtil;
import com.supply.hsa.link.common.constant.InitConfigData;
import com.supply.hsa.link.common.constant.SupplierClass;
import com.supply.hsa.link.task.fetch.CommonConstant;
import com.supply.hsa.link.task.fetch.hotel.FetchHotelDayTask;
import com.supply.hsa.link.task.fetch.TaskExecutor;
import com.supply.hsa.link.task.fetch.enums.HotelDataTypeEnum;
import com.supply.hsa.link.task.fetch.process.SupplyFetchDataService;
import com.supply.hsa.link.task.fetch.hotel.FetchHotelTask;
import com.supply.hsa.link.task.fetch.hotel.container.SupplyFetchDataServiceContainer;
import com.supply.hsa.link.task.fetch.hotel.container.ThreadPoolContainer;
import com.supply.hsa.link.task.fetch.hotel.dto.CommonSupplyFetchResultWrapper;
import com.supply.hsa.link.task.fetch.hotel.dto.CommonSyncRequest;
import com.supply.hsa.link.task.fetch.hotel.dto.CommonSyncResponse;
import com.supply.hsa.link.task.fetch.enums.ResponseCodeEnum;
import com.supply.hsa.link.task.fetch.server.CommonSupplyFetchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service("commonSupplyFetchService")
@Slf4j
public class CommonSupplyFetchServiceImpl implements CommonSupplyFetchService {

    @Autowired
    private SupplyFetchDataServiceContainer supplyDataServiceContainer;

    @Autowired
	private ThreadPoolContainer threadPoolContainer;
    
    @Autowired
    @Qualifier("fetchHotelDayTaskExecutor")
    private TaskExecutor<FetchHotelDayTask> fetchHotelDayTaskExecutor;


	@Override
	public CommonSyncResponse fetchHotelProductInfo(FetchHotelTask task){
		return this.fetchCommon(task, HotelDataTypeEnum.HotelInfo);
	}
	
	private CommonSyncResponse fetchCommon(FetchHotelTask task, HotelDataTypeEnum hotelDataType) {
		if (task == null || task.getRequest() == null || !task.getRequest().check()) {
			return new CommonSyncResponse(
					ResponseCodeEnum.FetchParamError.resultCode,
					ResponseCodeEnum.FetchParamError.message);
		}

		CommonSyncRequest request = task.getRequest();
		SupplierClass supplierClass = request.getSupplierClass();
		if (supplierClass == null) {
			return new CommonSyncResponse(
					ResponseCodeEnum.FetchParamError.resultCode,
					ResponseCodeEnum.FetchParamError.message);
		}

		SupplyFetchDataService supplyDataService = supplyDataServiceContainer
				.getSupplyFetchDataService(supplierClass);
		if (supplyDataService == null) {
			return new CommonSyncResponse(
					ResponseCodeEnum.FetchParamError.resultCode,
					ResponseCodeEnum.FetchParamError.message);
		}

		CommonSyncResponse response = new CommonSyncResponse(
				ResponseCodeEnum.Sucess.resultCode,
				ResponseCodeEnum.Sucess.message);
		response.setQueryStartTime(new Date());
		try {
			// 设置请求参数
			Map<String,String> lstConfigExtend = InitConfigData.shareSupplierMap.get(request.getSupplyCode());
			supplyDataService.fillingParamBeforeFetchData(request, lstConfigExtend);

			List<CommonSupplyFetchResultWrapper> resultList = this.fetchSupplyData(task, supplierClass, request, hotelDataType);
			response.setQueryEndTime(new Date());
			response.setHotelDataType(hotelDataType);
			response.setResultList(resultList);

			return response;
		} catch (Exception e) {
			String msg = null;
			if (hotelDataType == HotelDataTypeEnum.BasicHotelInfo) {
				msg = CommonConstant.Fetch_Hotel_Info_Exception + request.getSupplierClass();
			} else if (hotelDataType == HotelDataTypeEnum.HotelInfo) {
				msg = CommonConstant.Fetch_Product_Fail + request.getSupplierClass();
			}
			log.error(msg, e);
			
			response.setQueryEndTime(new Date());
			response.setResultCode(ResponseCodeEnum.Fail.resultCode);
			response.setFailReson(ResponseCodeEnum.Fail.message);
			
			return response;
		}
		
	}
	
	private List<CommonSupplyFetchResultWrapper> fetchSupplyData(
			FetchHotelTask task, SupplierClass supplierClass,
			CommonSyncRequest request, HotelDataTypeEnum hotelDataType)
			throws Exception {
		List<CommonSupplyFetchResultWrapper> resultList = null;
		List<CommonSyncRequest> commonSyncRequestList = this.parseCommonSyncRequest(request);
		ThreadPoolExecutor threadPoolExecutor = threadPoolContainer.getFetchThreadPoolExecutor(supplierClass);
		if(commonSyncRequestList.size()>1 && threadPoolExecutor!=null){
			//并发抓取数据
			List<Future<CommonSupplyFetchResultWrapper>> futureResultList = 
					new ArrayList<Future<CommonSupplyFetchResultWrapper>>(commonSyncRequestList.size());
			for(CommonSyncRequest commonSyncRequest : commonSyncRequestList){
				Future<CommonSupplyFetchResultWrapper> futureResult = threadPoolExecutor.submit(
						new SupplyFetchThread(task, commonSyncRequest, hotelDataType));
				futureResultList.add(futureResult);
			}
			
			resultList = new ArrayList<CommonSupplyFetchResultWrapper>(futureResultList.size());
			for(Future<CommonSupplyFetchResultWrapper> future : futureResultList){
				try {
					CommonSupplyFetchResultWrapper result = future.get(120, TimeUnit.SECONDS);//暂时默认限制超时120秒
					if(result!=null && CollectionUtils.isNotEmpty(result.getResultList())){
						resultList.add(result);
					}
				} catch (Exception e) {
					String msg = null;
					if (hotelDataType == HotelDataTypeEnum.BasicHotelInfo) {
						msg = CommonConstant.Fetch_Hotel_Info_Exception + request.getSupplierClass()+":"+request.getHotelId();
					} else if (hotelDataType == HotelDataTypeEnum.HotelInfo) {
						msg = CommonConstant.Fetch_Product_Fail + request.getSupplierClass()+":"+request.getHotelId();
					}
					log.error(msg, e);
				}
			}
		}else{
			resultList = new ArrayList<CommonSupplyFetchResultWrapper>(1);
			CommonSupplyFetchResultWrapper result = this.fetchInfo(task, request, hotelDataType);
			if(result!=null && CollectionUtils.isNotEmpty(result.getResultList())){
				resultList.add(result);
			}
		}
		
		return resultList;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private CommonSupplyFetchResultWrapper fetchInfo(FetchHotelTask task, CommonSyncRequest request, HotelDataTypeEnum hotelDataType){
		CommonSupplyFetchResultWrapper wrapper = new CommonSupplyFetchResultWrapper();
		wrapper.setRequest(request);
		wrapper.setQueryEndTime(new Date());
		
		FetchHotelDayTask fetchHotelDayTask = new FetchHotelDayTask();
		fetchHotelDayTask.setParentTaskId(task.getTaskId());
		fetchHotelDayTask.setRequest(request);
		fetchHotelDayTask.setHotelDataType(hotelDataType);
		fetchHotelDayTask.setSupplierClass(task.getSupplierClass());
		this.fetchHotelDayTaskExecutor.execute(fetchHotelDayTask);
		
		Object result = fetchHotelDayTask.getResponse();
		List<Object> resultList = null;
		if(result!=null){
			if(result instanceof Collection){
				Collection rCollection = (Collection)result;
				resultList = new ArrayList<Object>(rCollection.size());
				resultList.addAll(rCollection);
			}else{
				resultList = new ArrayList<Object>(1);
				resultList.add(result);
			}
		}
		wrapper.setResultList(resultList);
		wrapper.setQueryEndTime(new Date());
		wrapper.setSuccess(fetchHotelDayTask.isResult());
		
		return wrapper;
	}
	
	private class SupplyFetchThread implements Callable<CommonSupplyFetchResultWrapper>{
		private FetchHotelTask task;
		private CommonSyncRequest request;
		private HotelDataTypeEnum hotelDataType;
		public SupplyFetchThread(FetchHotelTask task, CommonSyncRequest request, HotelDataTypeEnum hotelDataType){
			this.task = task;
			this.request = request;
			this.hotelDataType = hotelDataType;
		}
		@Override
		public CommonSupplyFetchResultWrapper call() throws Exception {
			return fetchInfo(task, request, hotelDataType);
		}
	}
	
	private List<CommonSyncRequest> parseCommonSyncRequest(CommonSyncRequest request) throws Exception{
		List<CommonSyncRequest> commonSyncRequestList = null;
		Date checkInDate = request.getBeginDate();
		Date checkOutDate = request.getEndDate();
		int dayInterval = Long.valueOf(DateUtil.getDay(checkInDate, checkOutDate)).intValue();
		// 判断是否需要支持并发查询
		Integer concurrentQueryOfSplitQuery = request.getConcurrenyQueryOfSplitDay();
		if(concurrentQueryOfSplitQuery!=null && concurrentQueryOfSplitQuery>0 && dayInterval>1){
			int count = dayInterval%concurrentQueryOfSplitQuery == 0 
					? dayInterval/concurrentQueryOfSplitQuery
					: dayInterval/concurrentQueryOfSplitQuery+1;
			commonSyncRequestList = new ArrayList<CommonSyncRequest>(count);
			for(int i=1; i<=count; i++){
				CommonSyncRequest newCommonSyncRequest = (CommonSyncRequest)request.getClass().newInstance();
				PropertyCopyUtil.transfer(newCommonSyncRequest, CommonSyncRequest.class);
				
				newCommonSyncRequest.setBeginDate(checkInDate);
				
				if(i == count){
					newCommonSyncRequest.setEndDate(checkOutDate);
				}else{
					checkInDate = DateUtil.getDate(checkInDate, concurrentQueryOfSplitQuery, 0);
					newCommonSyncRequest.setEndDate(checkInDate);
				}
				
				commonSyncRequestList.add(newCommonSyncRequest);
			}
		}else{
			commonSyncRequestList = new ArrayList<CommonSyncRequest>(1);
			commonSyncRequestList.add(request);
		}
		
		return commonSyncRequestList;
	}
	
}
